Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($http): JSON parse failure #15724

Closed
wants to merge 6 commits into from
Closed

Conversation

chirag64
Copy link
Contributor

@chirag64 chirag64 commented Feb 19, 2017

Fixes #15695

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Bug fix

What is the current behavior? (You can also link to an open issue here)
#15695

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change?
No

Please check if the PR fulfills these requirements

Other information:

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

1 similar comment
@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@chirag64
Copy link
Contributor Author

I signed it!

@googlebot
Copy link

CLAs look good, thanks!

1 similar comment
@googlebot
Copy link

CLAs look good, thanks!

@fullName Bad JSON Data
@description

This error occurs when the data parameter passed to the {@link ng.$http `defaultHttpResponseTransform`} service is not a valid JSON object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please limit lines to 100 chars.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link should point tot a specific section (e.g. ng.$httpProvider.defaults, ng.$http.defaults, or ng$http#default-transformations).

BTW, I just noticed that $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse are not documented. They should be.

@description

This error occurs when the data parameter passed to the {@link ng.$http `defaultHttpResponseTransform`} service is not a valid JSON object.
`defaultHttpResponseTransform` expects the first parameter as a valid JSON object if the second parameter of headers specifies a Content-Type of JSON.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mention defaultHttpResponseTransform, because it is private and the users don't know what it is.
Just say that "The default transformResponse will try to parse the response as JSON if the Content-Type header is application/json or the response looks like a valid JSON-stringified object or array".

This error occurs when the data parameter passed to the {@link ng.$http `defaultHttpResponseTransform`} service is not a valid JSON object.
`defaultHttpResponseTransform` expects the first parameter as a valid JSON object if the second parameter of headers specifies a Content-Type of JSON.

The error message should provide additional context such as the actual value of the parameter that was received.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actual value of the parameter that was received --> actual response

src/ng/http.js Outdated
try {
data = fromJson(tempData);
} catch (e) {
throw minErr('$http')('baddata', 'Data must be a valid JSON object. Received: {0}', data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have an $httpMinErr instance. Use that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please, embed the actual error message into the minErr.

@gkalpak
Copy link
Member

gkalpak commented Feb 20, 2017

For tests, you can "train" $httpBackend with an invalid response and call $http (which will indirectly invoke defaultHttpResponseTransform).

@Narretz
Copy link
Contributor

Narretz commented Feb 20, 2017

@chirag64 There's already a test in httpSpec that should show you how to test this, see my comment here: #15695 (comment)

In short, you don't access the http transform function directly. You call http, and define a mock json answer that will be parsed.

@fullName Bad JSON Data
@description

The default @{link ng$http#default-transformations `transformResponse`} will try to parse the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be ng.$http#... (with a dot after ng)?


To resolve this error, make sure you pass a valid JSON data object to `transformResponse`.

For more information, see the {@link ng$http#default-transformations `transformResponse`} service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not necessary, since we already link to default-transformations above imo.


The error message should provide additional context such as the actual response.

To resolve this error, make sure you pass a valid JSON data object to `transformResponse`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a valid JSON data object --> valid JSON data

Also, the correct resolution depends on the source of the problem. In some cases, one might need to send correct headers (to not include application/json for example).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

To resolve this error, make sure you pass valid JSON data to transformResponse or use the appropriate Content-Type instead of the specified application/json if passing non-JSON data.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

To resolve this error, make sure you pass valid JSON data to transformResponse or use an appropriate Content-Type header for non-JSON data.

it('should return JSON data with error message if JSON is invalid', function() {
var errCallback = jasmine.createSpy('error');
$httpBackend.expect('GET', '/url').respond('{abcd}', {'Content-Type': 'application/json'});
$http({method: 'GET', url: '/url'}).then(callback).catch(errCallback);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong indentation.

Nit: It is simpler to use $http.get('/url').

src/ng/http.js Outdated
data = fromJson(tempData);
} catch (e) {
throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
'Error occurred: "{1}"', data, e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: Error occurred --> Original error or Parse error

@Narretz
Copy link
Contributor

Narretz commented Feb 22, 2017

One thing we need to decide is if the original response data should be available in the thrown error, for recovery attempts. I think it's useful, but we don't really have the option for this. the data is only available in serialized form in the error (or is it), so we'd have to create a new API or wrap the error with a different response object.

@gkalpak
Copy link
Member

gkalpak commented Feb 22, 2017

@Narretz, not sure what you mean 😕 The original response is usually just text so we can include it in the error message without further serialization.

@chirag64
Copy link
Contributor Author

chirag64 commented Mar 2, 2017

@gkalpak Please let me know if you want me to make any changes on top of the last commit that I made.

@Narretz
Copy link
Contributor

Narretz commented Mar 2, 2017

@gkalpak My concern is that you cannot get the original response string out the minErr without some extra parsing, because the data passed to the minErr is stringified into the message template. So with this solution it's harder to get the actual response string in case you want to something with the response only.

@gkalpak
Copy link
Member

gkalpak commented Mar 3, 2017

I see. I am that concerned about that. If they just want to log it (e.g. to the server), the extra context won't hurt. And if they want to try and fix the issue or handle it in some other way, then they having their own tranformResponse sounds a better approach anyway.

But I don't feel too strongly about it.

The problem is that I don't think it is possible to somehow pass multiple values (e.g. our error and the response as searate entities). Unless you have a clever idea 😃

@Narretz
Copy link
Contributor

Narretz commented Mar 3, 2017

Ok, I think it's reasonable to implement your own transformer if you need recovery or something special.

@chirag64 there's a failing test that expects something else from a failed JSON transform at

angular.js/test/ng/httpSpec.js

Lines 1372 to 1383 in 3dc0096

it('should forward json deserialization errors to the http error handler',
function() {
var errCallback = jasmine.createSpy('error');
$httpBackend.expect('GET', '/url').respond('abcd', {'Content-Type': 'application/json'});
$http({method: 'GET', url: '/url'}).then(callback).catch(errCallback);
$httpBackend.flush();
expect(callback).not.toHaveBeenCalled();
expect(errCallback).toHaveBeenCalledOnce();
expect(errCallback.calls.mostRecent().args[0]).toEqual(jasmine.any(SyntaxError));
});
. Please

  • remove this test
  • move your added test at this position (in the section about default response transform).

@chirag64
Copy link
Contributor Author

chirag64 commented Mar 4, 2017

@Narretz done 👍🏼


$httpBackend.expect('GET', '/url').respond('abcd', {'Content-Type': 'application/json'});
$http({method: 'GET', url: '/url'}).then(callback).catch(errCallback);
$httpBackend.expect('GET', '/url').respond('{abcd}', {'Content-Type': 'application/json'});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've replaced the it function of forwarding json deserialization errors with mine. The above highlighted statements are pretty much similar to the old code except that we're using an object '{abcd}' within the string instead of a direct string 'abcd'. Are you referring to that string or something else?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine with me. @gkalpak wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chirag64, yes, I am referring to the string. Why change abcd to {abcd}?
I am mostly curious - I don't think it makes any difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make it look clear that we're passing an invalid JSON instead of passing a string.

@Narretz Narretz modified the milestones: Backlog, 1.6.4 Mar 8, 2017
@gkalpak
Copy link
Member

gkalpak commented Mar 13, 2017

LGTM. Leaving it to @Narretz to give the final LGTM.

@Narretz
Copy link
Contributor

Narretz commented Mar 14, 2017

LGTM, please merge @gkalpak

@gkalpak gkalpak closed this in c80fa1c Mar 14, 2017
ellimist pushed a commit to ellimist/angular.js that referenced this pull request Mar 15, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JSON parse failure in defaultHttpResponseTransform
4 participants